/**
 * 
 */
package br.unicamp.ic.lsd.wscaa.concurrent;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import br.unicamp.ic.lsd.wscaa.WSCAManager;
import br.unicamp.ic.lsd.wscaa.exception.WSDynamicException;

/**
 * @author renatomanzoni
 * 
 */
public class ConcurrentExceptionSync {
	private Queue<WSDynamicException> queue = new LinkedList<WSDynamicException>();
	private long timeWindowResolution;
	private TimeUnit unit;
	private boolean doneExceptionsHandling;
	private static final long TIME_WINDOW_RESOLUTION = 40L;
	private static ConcurrentExceptionSync instance = null;
	private final Lock lock = new ReentrantLock();
	private static Log logger = LogFactory
			.getLog(ConcurrentExceptionSync.class);

	//private List<Object> waitResolution;

	int k = 0;

	public static ConcurrentExceptionSync getInstance() {
		if (instance == null) // load the default configuration
			instance = new ConcurrentExceptionSync(TIME_WINDOW_RESOLUTION,
					TimeUnit.MILLISECONDS);

		return instance;
	}

	public void init(long timeWindowResolution, TimeUnit unit) {

		this.timeWindowResolution = timeWindowResolution;

		this.unit = unit;

//		waitResolution = new ArrayList<Object>();
	}

	public void refresh() {

		if (queue == null)
			queue = new LinkedList<WSDynamicException>();
		else {
			if (queue.size() > 0)
				throw new RuntimeException(
						"There are exceptions to be handling!!!");
			queue.clear();
		}

		this.doneExceptionsHandling = true;

	}

	private ConcurrentExceptionSync(long timeWindowResolution, TimeUnit unit) {

		init(timeWindowResolution, unit);

		refresh();

	}

	public synchronized void addException(WSDynamicException e) {

		queue.add(e);

		new Thread() {
			public void run() {
				syncException();
			}
		}.start();
	}

	private void syncException() {
		List<WSDynamicException> exceptions = null;
		if (lock.tryLock()) {
			if (logger.isTraceEnabled())
				logger.trace("Thread lock...");
			try {
				doneExceptionsHandling = false;
				// espera x mili segundos para os restantes das excecoes...
				// try {
				// Thread.sleep(timeWindowResolution);
				// } catch (InterruptedException e) {
				// e.printStackTrace();
				// }
				// Thread.currentThread().
				synchronized (this) {
					try {
						unit.timedWait(this, timeWindowResolution);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}

				}

				exceptions = new ArrayList<WSDynamicException>();

				// synchronized (exceptions) {
				while (queue.size() > 0)
					exceptions.add(queue.remove());
				// }
			} finally {
				if (logger.isTraceEnabled())
					logger.trace("Thread unlock...");
				lock.unlock();
			}
		}
		// TODO: deveria ser sincrono ou assincrono o metodo abaixo???
		if (exceptions != null && !exceptions.isEmpty())
			handleExceptions(exceptions);

	}

	private void handleExceptions(List<WSDynamicException> exceptions) {

		if (logger.isInfoEnabled())
			logger.info("Handling the exceptions...");

		// TODO: precisa passar um conjunto de exceptions para encontrar a
		// raiz
		// @SuppressWarnings("unused")
		// WSDynamicException e = resolutionException();

		for (Iterator<WSDynamicException> iterator = exceptions.iterator(); iterator
				.hasNext();) {

			Exception exception = iterator.next();

			if (logger.isDebugEnabled())
				logger.debug("Exceptions = " + exception);
		}

		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		logger.info("Handling the exceptions OK");

		doneExceptionsHandling = true;
		wakeUpThreads();

	}

	public boolean hasDoneExceptionHandling() {

		return doneExceptionsHandling;
	}

	@SuppressWarnings("unused")
	private WSDynamicException resolutionException() {
		throw new RuntimeException("Method not implemented");
	}

	private void wakeUpThreads() {
		WSCAManager.getInstance().notifyObjects();
//		for (Iterator<Object> iterator = waitResolution.iterator(); iterator
//				.hasNext();) {
//			Object type = iterator.next();
//			synchronized (type) {
//				type.notifyAll();
//			}
//
//		}

	}

//	public void addWait(Object obj) {
//		waitResolution.add(obj);
//	}

	// @Override
	// public void handleException(WSDynamicException e) {
	//
	// if (logger.isInfoEnabled())
	// logger.info("Adding exception be handler...");
	//
	// addException(e);
	//
	// }

}
